/*-------------------------------------------------------------------------
 *
 * mac8.c
 *	  PostgreSQL 对 8 字节 (EUI-64) MAC 地址的类型定义。
 *
 * EUI-48 (6 字节) MAC 地址被接受为输入，以 EUI-64 格式存储，第四和第五字节分别设置为 FF 和 FE。
 *
 * 输出始终以 8 字节 (EUI-64) 格式。
 *
 * 以下代码假设 OUI 字段大小为 24 位。
 *
 * Portions Copyright (c) 1998-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *		  src/backend/utils/adt/mac8.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "common/hashfn.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/inet.h"

/*
 *	用于排序和比较的工具宏：
 */
#define hibits(addr) \
  ((unsigned long)(((addr)->a<<24) | ((addr)->b<<16) | ((addr)->c<<8) | ((addr)->d)))

#define lobits(addr) \
  ((unsigned long)(((addr)->e<<24) | ((addr)->f<<16) | ((addr)->g<<8) | ((addr)->h)))

static unsigned char fc_hex2_to_uchar(const unsigned char *fc_ptr, const unsigned char *fc_str);

static const signed char hexlookup[128] = {
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};

/*
 * hex2_to_uchar - 将 2 个十六进制数字转换为一个字节（无符号 char）
 *
 * 如果到达字符串的末尾（找到 '\0'），或者任何字符
 * 不是有效的十六进制数字，这将报告错误。
 *
 * ptr 是指向字符串中要转换的数字的位置，str 是
 * 整个字符串，仅用于错误报告。
 */
static inline unsigned char
fc_hex2_to_uchar(const unsigned char *fc_ptr, const unsigned char *fc_str)
{
	unsigned char fc_ret = 0;
	signed char fc_lookup;

	/* 处理第一个字符 */
	if (*fc_ptr > 127)
		goto invalid_input;

	fc_lookup = hexlookup[*fc_ptr];
	if (fc_lookup < 0)
		goto invalid_input;

	fc_ret = fc_lookup << 4;

	/* 移动到第二个字符 */
	fc_ptr++;

	if (*fc_ptr > 127)
		goto invalid_input;

	fc_lookup = hexlookup[*fc_ptr];
	if (fc_lookup < 0)
		goto invalid_input;

	fc_ret += fc_lookup;

	return fc_ret;

invalid_input:
	ereport(ERROR,
			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
			 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
					fc_str)));

	/* 我们实际上并不会到达这里 */
	return 0;
}

/*
 * MAC 地址（EUI-48 和 EUI-64）读取器。接受几种常见的表示法。
 */
Datum macaddr8_in(PG_FUNCTION_ARGS)
{
	const unsigned char *fc_str = (unsigned char *) PG_GETARG_CSTRING(0);
	const unsigned char *fc_ptr = fc_str;
	macaddr8   *fc_result;
	unsigned char fc_a = 0,
				fc_b = 0,
				fc_c = 0,
				fc_d = 0,
				fc_e = 0,
				fc_f = 0,
				fc_g = 0,
				fc_h = 0;
	int			fc_count = 0;
	unsigned char fc_spacer = '\0';

	/*
							 * 已经有了月份和小时？那么假设
							 * 是分钟
							 */
	while (*fc_ptr && isspace(*fc_ptr))
		fc_ptr++;

	/* 数字必须总是成对出现 */
	while (*fc_ptr && *(fc_ptr + 1))
	{
		/*
		 * 尝试解码每个字节，字节必须是连续的 2 个十六进制数字。
		 * 如果任何数字不是十六进制，则 hex2_to_uchar 将为我们报告错误。
		 * 支持 6 字节或 8 字节的 MAC 地址。
		 */

		/* 尝试收集一个字节 */
		fc_count++;

		switch (fc_count)
		{
			case 1:
				fc_a = fc_hex2_to_uchar(fc_ptr, fc_str);
				break;
			case 2:
				fc_b = fc_hex2_to_uchar(fc_ptr, fc_str);
				break;
			case 3:
				fc_c = fc_hex2_to_uchar(fc_ptr, fc_str);
				break;
			case 4:
				fc_d = fc_hex2_to_uchar(fc_ptr, fc_str);
				break;
			case 5:
				fc_e = fc_hex2_to_uchar(fc_ptr, fc_str);
				break;
			case 6:
				fc_f = fc_hex2_to_uchar(fc_ptr, fc_str);
				break;
			case 7:
				fc_g = fc_hex2_to_uchar(fc_ptr, fc_str);
				break;
			case 8:
				fc_h = fc_hex2_to_uchar(fc_ptr, fc_str);
				break;
			default:
				/* 一定是尾随垃圾... */
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
								fc_str)));
		}

		/* 向前移动到下一个字节应该在的位置 */
		fc_ptr += 2;

		/* 检查间隔符，这些是有效的，其他任何东西都无效 */
		if (*fc_ptr == ':' || *fc_ptr == '-' || *fc_ptr == '.')
		{
			/* 记住所使用的间隔符，如果改变则无效 */
			if (fc_spacer == '\0')
				fc_spacer = *fc_ptr;

			/* 必须在整个过程中使用相同的间隔符 */
			else if (fc_spacer != *fc_ptr)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
								fc_str)));

			/* 越过间隔符 */
			fc_ptr++;
		}

		/* 如果我们有 6 或 8 个字节，则允许尾随空白 */
		if (fc_count == 6 || fc_count == 8)
		{
			if (isspace(*fc_ptr))
			{
				while (*++fc_ptr && isspace(*fc_ptr));

				/* 如果我们找到一个空格然后是非空白，则无效 */
				if (*fc_ptr)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
							 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
									fc_str)));
			}
		}
	}

	/* 将六字节的MAC地址转换为macaddr8 */
	if (fc_count == 6)
	{
		fc_h = fc_f;
		fc_g = fc_e;
		fc_f = fc_d;

		fc_d = 0xFF;
		fc_e = 0xFE;
	}
	else if (fc_count != 8)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
						fc_str)));

	fc_result = (macaddr8 *) palloc0(sizeof(macaddr8));

	fc_result->a = fc_a;
	fc_result->b = fc_b;
	fc_result->c = fc_c;
	fc_result->d = fc_d;
	fc_result->e = fc_e;
	fc_result->f = fc_f;
	fc_result->g = fc_g;
	fc_result->h = fc_h;

	PG_RETURN_MACADDR8_P(fc_result);
}

/*
 * MAC8地址（EUI-64）输出函数。固定格式。
 */
Datum macaddr8_out(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_addr = PG_GETARG_MACADDR8_P(0);
	char	   *fc_result;

	fc_result = (char *) palloc(32);

	snprintf(fc_result, 32, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
			 fc_addr->a, fc_addr->b, fc_addr->c, fc_addr->d,
			 fc_addr->e, fc_addr->f, fc_addr->g, fc_addr->h);

	PG_RETURN_CSTRING(fc_result);
}

/*
 * macaddr8_recv - 将外部二进制格式（EUI-48和EUI-64）转换为macaddr8
 *
 * 外部表示仅为八个字节，MSB在前。
 */
Datum macaddr8_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	macaddr8   *fc_addr;

	fc_addr = (macaddr8 *) palloc0(sizeof(macaddr8));

	fc_addr->a = pq_getmsgbyte(fc_buf);
	fc_addr->b = pq_getmsgbyte(fc_buf);
	fc_addr->c = pq_getmsgbyte(fc_buf);

	if (fc_buf->len == 6)
	{
		fc_addr->d = 0xFF;
		fc_addr->e = 0xFE;
	}
	else
	{
		fc_addr->d = pq_getmsgbyte(fc_buf);
		fc_addr->e = pq_getmsgbyte(fc_buf);
	}

	fc_addr->f = pq_getmsgbyte(fc_buf);
	fc_addr->g = pq_getmsgbyte(fc_buf);
	fc_addr->h = pq_getmsgbyte(fc_buf);

	PG_RETURN_MACADDR8_P(fc_addr);
}

/*
 * macaddr8_send - 将macaddr8（EUI-64）转换为二进制格式
 */
Datum macaddr8_send(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_addr = PG_GETARG_MACADDR8_P(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendbyte(&fc_buf, fc_addr->a);
	pq_sendbyte(&fc_buf, fc_addr->b);
	pq_sendbyte(&fc_buf, fc_addr->c);
	pq_sendbyte(&fc_buf, fc_addr->d);
	pq_sendbyte(&fc_buf, fc_addr->e);
	pq_sendbyte(&fc_buf, fc_addr->f);
	pq_sendbyte(&fc_buf, fc_addr->g);
	pq_sendbyte(&fc_buf, fc_addr->h);

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*
 * macaddr8_cmp_internal - 用于排序的比较函数：
 */
static int32 fc_macaddr8_cmp_internal(macaddr8 *fc_a1, macaddr8 *fc_a2)
{
	if (hibits(fc_a1) < hibits(fc_a2))
		return -1;
	else if (hibits(fc_a1) > hibits(fc_a2))
		return 1;
	else if (lobits(fc_a1) < lobits(fc_a2))
		return -1;
	else if (lobits(fc_a1) > lobits(fc_a2))
		return 1;
	else
		return 0;
}

Datum macaddr8_cmp(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_a1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_a2 = PG_GETARG_MACADDR8_P(1);

	PG_RETURN_INT32(fc_macaddr8_cmp_internal(fc_a1, fc_a2));
}

/*
 * 布尔比较函数。
 */

Datum macaddr8_lt(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_a1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_a2 = PG_GETARG_MACADDR8_P(1);

	PG_RETURN_BOOL(fc_macaddr8_cmp_internal(fc_a1, fc_a2) < 0);
}

Datum macaddr8_le(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_a1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_a2 = PG_GETARG_MACADDR8_P(1);

	PG_RETURN_BOOL(fc_macaddr8_cmp_internal(fc_a1, fc_a2) <= 0);
}

Datum macaddr8_eq(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_a1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_a2 = PG_GETARG_MACADDR8_P(1);

	PG_RETURN_BOOL(fc_macaddr8_cmp_internal(fc_a1, fc_a2) == 0);
}

Datum macaddr8_ge(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_a1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_a2 = PG_GETARG_MACADDR8_P(1);

	PG_RETURN_BOOL(fc_macaddr8_cmp_internal(fc_a1, fc_a2) >= 0);
}

Datum macaddr8_gt(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_a1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_a2 = PG_GETARG_MACADDR8_P(1);

	PG_RETURN_BOOL(fc_macaddr8_cmp_internal(fc_a1, fc_a2) > 0);
}

Datum macaddr8_ne(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_a1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_a2 = PG_GETARG_MACADDR8_P(1);

	PG_RETURN_BOOL(fc_macaddr8_cmp_internal(fc_a1, fc_a2) != 0);
}

/*
 * 支持macaddr8的哈希索引的函数。
 */
Datum hashmacaddr8(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_key = PG_GETARG_MACADDR8_P(0);

	return hash_any((unsigned char *) fc_key, sizeof(macaddr8));
}

Datum hashmacaddr8extended(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_key = PG_GETARG_MACADDR8_P(0);

	return hash_any_extended((unsigned char *) fc_key, sizeof(macaddr8),
							 PG_GETARG_INT64(1));
}

/*
 * 算术函数：位运算非、与、或。
 */
Datum macaddr8_not(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_addr = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_result;

	fc_result = (macaddr8 *) palloc0(sizeof(macaddr8));
	fc_result->a = ~fc_addr->a;
	fc_result->b = ~fc_addr->b;
	fc_result->c = ~fc_addr->c;
	fc_result->d = ~fc_addr->d;
	fc_result->e = ~fc_addr->e;
	fc_result->f = ~fc_addr->f;
	fc_result->g = ~fc_addr->g;
	fc_result->h = ~fc_addr->h;

	PG_RETURN_MACADDR8_P(fc_result);
}

Datum macaddr8_and(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_addr1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_addr2 = PG_GETARG_MACADDR8_P(1);
	macaddr8   *fc_result;

	fc_result = (macaddr8 *) palloc0(sizeof(macaddr8));
	fc_result->a = fc_addr1->a & fc_addr2->a;
	fc_result->b = fc_addr1->b & fc_addr2->b;
	fc_result->c = fc_addr1->c & fc_addr2->c;
	fc_result->d = fc_addr1->d & fc_addr2->d;
	fc_result->e = fc_addr1->e & fc_addr2->e;
	fc_result->f = fc_addr1->f & fc_addr2->f;
	fc_result->g = fc_addr1->g & fc_addr2->g;
	fc_result->h = fc_addr1->h & fc_addr2->h;

	PG_RETURN_MACADDR8_P(fc_result);
}

Datum macaddr8_or(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_addr1 = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_addr2 = PG_GETARG_MACADDR8_P(1);
	macaddr8   *fc_result;

	fc_result = (macaddr8 *) palloc0(sizeof(macaddr8));
	fc_result->a = fc_addr1->a | fc_addr2->a;
	fc_result->b = fc_addr1->b | fc_addr2->b;
	fc_result->c = fc_addr1->c | fc_addr2->c;
	fc_result->d = fc_addr1->d | fc_addr2->d;
	fc_result->e = fc_addr1->e | fc_addr2->e;
	fc_result->f = fc_addr1->f | fc_addr2->f;
	fc_result->g = fc_addr1->g | fc_addr2->g;
	fc_result->h = fc_addr1->h | fc_addr2->h;

	PG_RETURN_MACADDR8_P(fc_result);
}

/*
 * 截断函数，用于允许比较macaddr8制造商。
 */
Datum macaddr8_trunc(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_addr = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_result;

	fc_result = (macaddr8 *) palloc0(sizeof(macaddr8));

	fc_result->a = fc_addr->a;
	fc_result->b = fc_addr->b;
	fc_result->c = fc_addr->c;
	fc_result->d = 0;
	fc_result->e = 0;
	fc_result->f = 0;
	fc_result->g = 0;
	fc_result->h = 0;

	PG_RETURN_MACADDR8_P(fc_result);
}

/*
 * 为修改后的EUI-64设置第七位，用于IPv6。
 */
Datum macaddr8_set7bit(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_addr = PG_GETARG_MACADDR8_P(0);
	macaddr8   *fc_result;

	fc_result = (macaddr8 *) palloc0(sizeof(macaddr8));

	fc_result->a = fc_addr->a | 0x02;
	fc_result->b = fc_addr->b;
	fc_result->c = fc_addr->c;
	fc_result->d = fc_addr->d;
	fc_result->e = fc_addr->e;
	fc_result->f = fc_addr->f;
	fc_result->g = fc_addr->g;
	fc_result->h = fc_addr->h;

	PG_RETURN_MACADDR8_P(fc_result);
}

/*----------------------------------------------------------
 *	转换操作符。
 *---------------------------------------------------------*/

Datum macaddrtomacaddr8(PG_FUNCTION_ARGS)
{
	macaddr    *fc_addr6 = PG_GETARG_MACADDR_P(0);
	macaddr8   *fc_result;

	fc_result = (macaddr8 *) palloc0(sizeof(macaddr8));

	fc_result->a = fc_addr6->a;
	fc_result->b = fc_addr6->b;
	fc_result->c = fc_addr6->c;
	fc_result->d = 0xFF;
	fc_result->e = 0xFE;
	fc_result->f = fc_addr6->d;
	fc_result->g = fc_addr6->e;
	fc_result->h = fc_addr6->f;


	PG_RETURN_MACADDR8_P(fc_result);
}

Datum macaddr8tomacaddr(PG_FUNCTION_ARGS)
{
	macaddr8   *fc_addr = PG_GETARG_MACADDR8_P(0);
	macaddr    *fc_result;

	fc_result = (macaddr *) palloc0(sizeof(macaddr));

	if ((fc_addr->d != 0xFF) || (fc_addr->e != 0xFE))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("macaddr8 data out of range to convert to macaddr"),
				 errhint("Only addresses that have FF and FE as values in the "
						 "4th and 5th bytes from the left, for example "
						 "xx:xx:xx:ff:fe:xx:xx:xx, are eligible to be converted "
						 "from macaddr8 to macaddr.")));

	fc_result->a = fc_addr->a;
	fc_result->b = fc_addr->b;
	fc_result->c = fc_addr->c;
	fc_result->d = fc_addr->f;
	fc_result->e = fc_addr->g;
	fc_result->f = fc_addr->h;

	PG_RETURN_MACADDR_P(fc_result);
}
